#import "BWBackStitchLayer.h"
#import "BWGridGeometry.h"
#import "BWColorKey.h"
#import "BWBackStitchSet.h"
#import "BWBackStitchChangeList.h"
#import "BWThread.h"

#import "BWGraphDocument.h" // scaffolding



@implementation BWBackStitchLayer

+ (void) initialize
{
    [super initialize];
    [BWBackStitchLayer setVersion: 1];

} // initalize


- (id) init
{
    if (self = [super init]) {
        
    }

    return (self);

} // init


- (void) dealloc
{
    [backStitchSet release];
    [super dealloc];

} // dealloc


- (void) addStitchesForDebugging
{
    BWColorKey *colorKey = [g_globalDocument colorKey];
    NSArray *colorKeyArray = [colorKey threadArrayThingie];

    int count = [colorKeyArray count];

    int i;
    for (i = 0; i < 10; i++) {
        int row = random() % [geometry countDown];
        int column = random() % [geometry countAcross];

        int row2 = row + (random() % 20);
        int column2 = column + (random() % 30);

        if (row2 > [geometry countDown]) {
            row2 = [geometry countDown] - 1;
        }

        if (column2 > [geometry countAcross]) {
            column2 = [geometry countAcross] - 1;
        }

        int color = random() % count;

        [backStitchSet addBackStitchFromRow: row
                       column: column
                       quadrant: kQuadrant_Center
                       toRow: row2
                       column: column2
                       quadrant: kQuadrant_Center
                       thread: [colorKeyArray objectAtIndex: color]];
    }

    [self postChangeNotification];
    
} // addStitchesForDebugging


- (void) setGeometry: (BWGridGeometry *) geo
{
    [super setGeometry: geo];

    if (backStitchSet == nil) {
        backStitchSet = [[BWBackStitchSet alloc] initWithGeometry: geo];
    }

} // setGeometry


- (void) getStartPoint: (NSPoint *) startPoint
              endPoint: (NSPoint *) endPoint
             forStitch: (BWBackStitch *) stitch
{
    *startPoint = [geometry pointForIntersectionAtRow: [stitch startRow]
                            column: [stitch startColumn]
                            quadrant: [stitch startQuadrant]];
    startPoint->x -= 0.5;
    startPoint->y -= 0.5;

    *endPoint = [geometry pointForIntersectionAtRow: [stitch endRow]
                          column: [stitch endColumn]
                          quadrant: [stitch endQuadrant]];

    endPoint->x -= 0.5;
    endPoint->y -= 0.5;

} // getStartPoint


- (void) drawStitch: (BWBackStitch *) stitch
{
    if (![self active]) {
        [[NSColor lightGrayColor] set];
    } else {
        BWThread *thread = [stitch thread];
        [[[thread color] color] set];
    }

    NSPoint startPoint, endPoint;
    [self getStartPoint: &startPoint
          endPoint: &endPoint
          forStitch: stitch];

    // !!! this is starting to get a bit ugly
    if (stitch == selectedStitch && overridePart != kPart_None) {
        if (overridePart == kPart_Start) {
            startPoint = [geometry pointForIntersectionAtRow: overrideRow
                                   column: overrideColumn
                                   quadrant: overrideQuadrant];
            startPoint.x -= 0.5;
            startPoint.y -= 0.5;

        } else if (overridePart == kPart_End) {
            endPoint = [geometry pointForIntersectionAtRow: overrideRow
                                 column: overrideColumn
                                 quadrant: overrideQuadrant];
            endPoint.x -= 0.5;
            endPoint.y -= 0.5;
        }
    }

    NSBezierPath *path = [[NSBezierPath alloc] init];

    // setDefaultLineWidth is not saved by saveGraphicsState!
    // Ack!  so set the attrs on the path directly

    [path moveToPoint: startPoint];
    [path lineToPoint: endPoint];
    if (stitch == selectedStitch) {
        [path setLineWidth: 5.0];
    } else {
        [path setLineWidth: 3.0];
    }
    [path setLineCapStyle: NSRoundLineCapStyle];

    [path stroke];
    [path release];

} // drawStitch


- (void) drawRect: (NSRect) rect
{
    int i;
    for (i = 0; i < [changeList count]; i++) {
        BWBackStitch *stitch = [changeList stitchAtIndex: i];
        [self drawStitch: stitch];
    }

    for (i = 0; i < [backStitchSet count]; i++) {
        BWBackStitch *stitch = [backStitchSet stitchAtIndex: i];
        [self drawStitch: stitch];
    }

} // drawRect


- (BWLayerKind) layerKind
{
    return (kLayerKind_BackStitch);
} // layerKind


- (void) setChangeList: (BWBackStitchChangeList *) cl
{
    [cl retain];
    [changeList release];

    changeList = cl;

} // setChangeList


- (void) applyChanges
{
    [backStitchSet applyChangeList: changeList];
} // applyChangeList



- (BOOL) point: (NSPoint) point  onLineFromPoint: (NSPoint) startPoint
       toPoint: (NSPoint) endPoint
          part: (BWBackStitch_Part *) part
{
    BOOL result = YES;

    // found on an internet forum.
    // An easier way to put that is to determine if the sum of the
    // distances from the point to the endpoints
    // is less than the distance between the endpoints plus some small delta
    
    float distPS, distPE, distSE; // Point, Start, End

    // move point to zero, zero so we can use hypot
    distPS = hypot (point.x - startPoint.x,
                    point.y - startPoint.y);
    distPE = hypot (point.x - endPoint.x,
                    point.y - endPoint.y);
    distSE = hypot (startPoint.x - endPoint.x,
                    startPoint.y - endPoint.y);

    float sumDist = distPS + distPE;

    if (distPS < 5.0) {
        *part = kPart_Start;

    } else if (distPE < 5.0) {
        *part = kPart_End;

    } else if (sumDist < distSE + 0.25) {
        *part = kPart_Middle;

    } else {
        result = NO;
    }

    return (result);

} // point onLine



- (BWBackStitch *) stitchUnderPoint: (NSPoint) point
                               part: (BWBackStitch_Part *) part
{
    int i;
    BWBackStitch *stitch = nil;

    if (part) {
        *part = kPart_None;
    }

    for (i = 0; i < [backStitchSet count]; i++) {
        BWBackStitch *candidateStitch = [backStitchSet stitchAtIndex: i];
        BWBackStitch_Part candidatePart;
        
        NSPoint startPoint, endPoint;
        [self getStartPoint: &startPoint
              endPoint: &endPoint
              forStitch: candidateStitch];

        if ([self point: point
                  onLineFromPoint: startPoint
                  toPoint: endPoint
                  part: &candidatePart]) {
            stitch = candidateStitch;

            if (part) {
                *part = candidatePart;
            }
            break;
        }
    }

    return (stitch);
    
} // stitchUnderPoint



- (void) selectStitch: (BWBackStitch *) stitch
{
    if (stitch != selectedStitch) {
        [stitch retain];
        [selectedStitch release];
        selectedStitch = stitch;

        overridePart = kPart_None;

        [self postChangeNotification];
    }

} // selectStitch


- (void) setOverrideForPart: (BWBackStitch_Part) part
                      toRow: (int) row
                     column: (int) column
                   quadrant: (BWGrid_Quadrant) quadrant
{
    overridePart = part;
    overrideRow = row;
    overrideColumn = column;
    overrideQuadrant = quadrant;

    [self postChangeNotification];

} // setOverrideForPart


- (void) commitOverride
{
    if (selectedStitch != nil) {
        if (overridePart == kPart_Start) {
            [selectedStitch setStartRow: overrideRow
                            column: overrideColumn
                            quadrant: overrideQuadrant];
        } else if (overridePart == kPart_End) {
            [selectedStitch setEndRow: overrideRow
                            column: overrideColumn
                            quadrant: overrideQuadrant];
        }
        
        [self postChangeNotification];
    }

} // commitOverride


- (void) removeSelection
{
    // ???make a change list for this?

    [backStitchSet removeStitch: selectedStitch];

    [self postChangeNotification];

} // removeSelection


- (void) replaceThread: (BWThread *) search
            withThread: (BWThread *) replace
{
    int i;
    for (i = 0; i < [backStitchSet count]; i++) {
        BWBackStitch *stitch = [backStitchSet stitchAtIndex: i];

        if ([stitch thread] == search) {
            [stitch setThread: replace];
        }
    }
        
    [self postChangeNotification];

} // replaceThread



- (void) encodeWithCoder: (NSCoder *) coder
{
    [super encodeWithCoder: coder];

    [coder encodeObject: backStitchSet];

} // encodeWithCoder


- (id) initWithCoder: (NSCoder *) coder
{
    if (self = [super initWithCoder: coder]) {
        backStitchSet = [[coder decodeObject] retain];
    }

    return (self);

} // initWithCoder


@end // BWBackStitchLayer

